package com.john.study.es.config.parser;

import com.google.gson.JsonObject;
import com.john.common.util.JohnBeanUtils;
import com.john.study.es.config.BooleanEnum;
import com.john.study.es.config.annotation.FieldMapping;
import com.john.study.es.config.annotation.FieldMappingAll;
import com.john.study.es.config.annotation.FieldsItem;
import com.john.study.es.config.annotation.MappingBean;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.List;

/**
 * @author jiangguangtao 2017/7/28.
 */
public class JsonMappingParser {
    public static final String ANALYZER_TYPE_STANDARD = "standard";

    /**
     * @param typeName 类型名称，可选的，不传入时使用类注解，传入时覆盖
     * @param voType   要生成映射的对象
     * @return 解释出的映射JSON串
     * @throws IllegalArgumentException 如果参数错误或者指定的Bean无映射定义
     */
    public JsonObject parseJson(String typeName, Class<?> voType) {
        if (null == voType) {
            return null;
        }

        MappingBean mappingBean = voType.getAnnotation(MappingBean.class);
        if (StringUtils.isBlank(typeName) && null != mappingBean) {
            typeName = mappingBean.value();
            if (StringUtils.isBlank(typeName)) {
                typeName = mappingBean.type();
            }
        }
        if (StringUtils.isBlank(typeName)) {
            throw new IllegalArgumentException("请指定映射的类型名称");
        }
        JsonObject mapping = new JsonObject();

        FieldMappingAll fieldMappingAll = voType.getAnnotation(FieldMappingAll.class);

        JsonObject allMapping = processAllMapping(fieldMappingAll);
        if (null != allMapping) {
            mapping.add("_all", allMapping);
        }

        List<Field> fields = JohnBeanUtils.getAllFields(voType, Modifier.STATIC | Modifier.FINAL);

        // 字段的映射集合
        JsonObject props = new JsonObject();
        for (Field field : fields) {
            FieldMapping fma = field.getAnnotation(FieldMapping.class);
            JsonObject fieldMapping = processField(fma);
            if (null != fieldMapping) {
                String name = fma.name();
                if (StringUtils.isBlank(name)) {
                    name = field.getName();
                }
                props.add(name, fieldMapping);
            }
        }
        if (props.size() == 0) {
            throw new IllegalArgumentException("传入的类型" + voType.getName() + "未定义映射字段！");
        }

        mapping.add("properties", props);

        return mapping;
    }

    /**
     * @param typeName 类型名称，可选的，不传入时使用类注解，传入时覆盖
     * @param voType   要生成映射的对象
     * @return 解释出的映射JSON串
     * @throws IllegalArgumentException 如果参数错误或者指定的Bean无映射定义
     */
    public String parseString(String typeName, Class<?> voType) {
        JsonObject mapping = parseJson(typeName, voType);
        if (null != mapping) {
            return mapping.toString();
        }
        return null;
    }

    /**
     * 获取字段的映射定义JSON
     *
     * @param fma
     * @return
     */
    private JsonObject processField(FieldMapping fma) {
        if (null == fma) {
            return null;
        }
        JsonObject mapping = new JsonObject();
        mapping.addProperty("type", fma.type().getValue());

        if (fma.boost() != -1.0f) {
            mapping.addProperty("boost", fma.boost());
        }

        if (StringUtils.isNotBlank(fma.analyzer())) {
            mapping.addProperty("analyzer", fma.analyzer());
        }
        if (StringUtils.isNotBlank(fma.searchAnalyzer())) {
            mapping.addProperty("search_analyzer", fma.searchAnalyzer());
        }
        if (StringUtils.isNotBlank(fma.format())) {
            mapping.addProperty("format", fma.format());
        }
        if (StringUtils.isNotBlank(fma.similarity())) {
            mapping.addProperty("similarity", fma.similarity());
        }

        if (!fma.index().equals(BooleanEnum.DEFAULT)) {
            mapping.addProperty("index", fma.index().getValue());
        }
        if (!fma.enabled().equals(BooleanEnum.DEFAULT)) {
            mapping.addProperty("enabled", fma.enabled().getValue());
        }
        if (!fma.ignoreMalformed().equals(BooleanEnum.DEFAULT)) {
            mapping.addProperty("ignore_malformed", fma.ignoreMalformed().getValue());
        }
        if (!fma.eagerGlobalOrdinals().equals(BooleanEnum.DEFAULT)) {
            mapping.addProperty("eager_global_ordinals", fma.eagerGlobalOrdinals().getValue());
        }
        if (!fma.fielddata().equals(BooleanEnum.DEFAULT)) {
            mapping.addProperty("eager_global_ordinals", fma.fielddata().getValue());
        }
        if (!fma.norms().equals(BooleanEnum.DEFAULT)) {
            mapping.addProperty("norms", fma.norms().getValue());
        }
        if (!fma.docValues().equals(BooleanEnum.DEFAULT)) {
            mapping.addProperty("doc_values", fma.docValues().getValue());
        }

        if (fma.fields().length > 0) {
            JsonObject fieldsObj = new JsonObject();

            for (FieldsItem fieldsItem : fma.fields()) {
                JsonObject fiObj = new JsonObject();
                if (StringUtils.isNotBlank(fieldsItem.type())) {
                    fiObj.addProperty("type", fieldsItem.type());
                }
                if (StringUtils.isNotBlank(fieldsItem.analyzer())) {
                    fiObj.addProperty("type", fieldsItem.type());
                }
                if (fiObj.size() > 0) {
                    fieldsObj.add(fieldsItem.name(), fiObj);
                }
            }

            if (fieldsObj.size() > 0) {
                mapping.add("fields", fieldsObj);
            }
        }

        if (mapping.size() > 0) {
            return mapping;
        }
        return null;
    }

    /**
     * 处理映射的默认字段
     *
     * @param fieldMappingAll
     * @return
     */
    private JsonObject processAllMapping(FieldMappingAll fieldMappingAll) {
        if (null == fieldMappingAll) {
            return null;
        }
        JsonObject mapping = new JsonObject();

        if (StringUtils.isNotBlank(fieldMappingAll.analyzer())) {
            mapping.addProperty("analyzer", fieldMappingAll.analyzer());
        }
        if (!fieldMappingAll.index().equals(BooleanEnum.DEFAULT)) {
            mapping.addProperty("index", fieldMappingAll.index().getValue());
        }
        if (!fieldMappingAll.enabled().equals(BooleanEnum.DEFAULT)) {
            mapping.addProperty("enabled", fieldMappingAll.enabled().getValue());
        }

        if (mapping.size() > 0) {
            return mapping;
        }
        return null;
    }

}
